package DPOP;

import java.io.File;
import java.util.StringTokenizer;

import org.w3c.dom.*;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException; 
class XMLReader {
	boolean DEBUG = true;
	String problemName = new String();
	String agentName = new String();
	int maxConstraintArity = 0;
	boolean maximize = false;
	int nbNeighbors = 0;
	Neighbors neighbors[];
	int nbVariables = 0;
	String variables[][];
	int nbConstraints = 0;
	Constraints constraints[];

	public XMLReader(String fileName, boolean b) {
		try {
			DEBUG = b;
			DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
			Document doc = docBuilder.parse (new File(fileName));
			doc.getDocumentElement ().normalize ();

			problemName =  ((Element)doc.getElementsByTagName("presentation").item(0)).getAttribute("name");
			maxConstraintArity = Integer.parseInt(((Element)doc.getElementsByTagName("presentation").item(0)).getAttribute("maxConstraint"));
			maximize = Boolean.parseBoolean(((Element)doc.getElementsByTagName("presentation").item(0)).getAttribute("maximize"));
			problemName =  ((Element)doc.getElementsByTagName("agent").item(0)).getAttribute("name");
			// Get the neighbor info
			Node n1 = doc.getElementsByTagName("neighbors").item(0);
			Element e1 = (Element)n1;
			nbNeighbors  = Integer.parseInt(e1.getAttribute("nbNeighbors"));
			neighbors = new Neighbors[nbNeighbors];
			agentName =  ((Element)doc.getElementsByTagName("agent").item(0)).getAttribute("name");
			myPrintln("Agent Name = "+agentName);
			for (int i=0; i<nbNeighbors;i++) {
				Element e2 = (Element) (e1.getElementsByTagName("neighbor")).item(i);
				int nbRelatedVariables = e2.getElementsByTagName("relatedVar").getLength();
				neighbors[i] = new Neighbors(nbRelatedVariables);
				neighbors[i].agentName = e2.getAttribute("name");
				neighbors[i].commCost = e2.getAttribute("commCost");

				for (int j=0; j<nbRelatedVariables; j++) {
					Element e3 = (Element) (e2.getElementsByTagName("relatedVar")).item(j);
					neighbors[i].relatedVariables[j][0] = e3.getAttribute("name");
					String stemp = e3.getAttribute("domain");
					neighbors[i].relatedVariables[j][1] = stemp.substring(0, stemp.indexOf("."));
					neighbors[i].relatedVariables[j][2] = stemp.substring(stemp.lastIndexOf(".")+1, stemp.length());
				}
			} // end outer for

			for (int i=0; i<neighbors.length;i++) {
				myPrintln("Neighbor Agent    = "+neighbors[i].agentName);
				myPrintln("Neighbor CommCost = "+neighbors[i].commCost);
				for (int j=0; j<neighbors[i].relatedVariables.length;j++) {
					myPrintln("\tVariable Name = "+neighbors[i].relatedVariables[j][0]);
					myPrintln("\tLower CapVal  = "+neighbors[i].relatedVariables[j][1]);
					myPrintln("\tUpper CapVal  = "+neighbors[i].relatedVariables[j][2]);
				} 
			} // end outer for

			// Get the variables
			e1 = (Element)(doc.getElementsByTagName("variables").item(0));
			NodeList domainNodeList = ((Element)(doc.getElementsByTagName("domains").item(0))).getElementsByTagName("domain");

			nbVariables = Integer.parseInt(e1.getAttribute("nbVariables"));
			variables = new String[nbVariables][3];

			for (int i=0; i<nbVariables; i++) {
				Element e2 = (Element)(e1.getElementsByTagName("variable").item(i));
				//Element e3 = (Element)(e1.getElementsByTagName("domain").item(i));
				variables[i][0] = e2.getAttribute("name");
				//myPrintln(variables[i][0]);
				//find correct domain for the variable
				for (int j=0; j<domainNodeList.getLength(); j++) {
					if (((Element)(domainNodeList.item(j))).getAttribute("name").equals(e2.getAttribute("domain"))) {
						String temp = (((Element)(domainNodeList.item(j))).getFirstChild().getNodeValue());
						variables[j][1] = temp.substring(0, temp.indexOf("."));
						variables[j][2] = temp.substring(temp.lastIndexOf(".")+1, temp.length());
						break; 
					}
				}
			} // end outer for

			for (int i=0; i<variables.length; i++)
				myPrintln(variables[i][0]+"("+variables[i][1]+".."+variables[i][2]+")");

			// Get the constraints
			e1 = (Element)(doc.getElementsByTagName("constraints").item(0));
			NodeList relationNodeList = ((Element)(doc.getElementsByTagName("relations").item(0))).getElementsByTagName("relation");

			int nbConstraints = Integer.parseInt(e1.getAttribute("nbConstraints"));
			constraints = new Constraints[nbConstraints];

			for (int i=0; i<nbConstraints; i++) {
				Element e2 = (Element)(e1.getElementsByTagName("constraint").item(i));

				int nbTuples = 0;
				String defaultCost = new String();
				String tuples = new String();
				//find correct domain for the variable
				for (int j=0; j<relationNodeList.getLength(); j++) {
					if (((Element)(relationNodeList.item(j))).getAttribute("name").equals(e2.getAttribute("reference"))) {
						tuples = (((Element)(relationNodeList.item(j))).getFirstChild().getNodeValue());
						nbTuples = Integer.parseInt(((Element)(relationNodeList.item(j))).getAttribute("nbTuples"));
						defaultCost = ((Element)(relationNodeList.item(j))).getAttribute("defaultCost");
						break; 
					}
				}

				
				constraints[i] = new Constraints(Integer.parseInt(e2.getAttribute("arity")), nbTuples);
				constraints[i].constraintName = e2.getAttribute("name");
				constraints[i].arity = Integer.parseInt(e2.getAttribute("arity"));
				constraints[i].defaultCost = defaultCost;

				String scopeTemp = e2.getAttribute("scope");
				StringTokenizer StrTkn = new StringTokenizer(scopeTemp.trim(), " ");
				
				/**
				 * TODO: check the array size. ArrayIndexOutOfBound here for N2.
				 * **/
				int x = 0;
				while(StrTkn.hasMoreTokens()) {
					constraints[i].scope[x] = new String();
					constraints[i].scope[x++] = StrTkn.nextToken();
					//System.out.println(agentName+": constraint name = "+e2.getAttribute("name")+"\tnbTuples = "+nbTuples
					//		+ "scope = "+constraints[i].scope[x-1]);
				}

				x = 0;
				String temp1 = new String();
				String temp2 = new String();
				int cost = 0;

				StrTkn = new StringTokenizer(tuples.trim(), "|");
				int j=0;
				while (StrTkn.hasMoreTokens()) {
					temp1 = StrTkn.nextToken().trim();
					if (temp1.contains(":")) {
						cost = Integer.parseInt(temp1.substring(0, temp1.indexOf(':')).trim());
						temp2 = temp1.substring(temp1.indexOf(':')+1, temp1.length()).trim();
					}
					else {// if value is not given then use the last value
						temp2 = "";
					}
					if (temp2.length()>=1)  // adjust string if value is given
						temp1 = temp2;

					constraints[i].table[j][0] = cost;

					StringTokenizer st2 = new StringTokenizer(temp1, " ");
					int k = 1;
					while (st2.hasMoreTokens()) {
						constraints[i].table[j][k++] = Integer.parseInt(st2.nextToken());
					}
					j++;
				} // end outer while
			} // end outer for

			// print constraints
			myPrintln("Constraints: ");
			for (int i=0; i<constraints.length; i++) {
				myPrintln("\tname = "+constraints[i].constraintName);
				myPrintln("\tarity = "+constraints[i].arity);
				myPrintln("\tdefault cost = "+constraints[i].defaultCost);
				myPrint("\tscope = ");
				for (int j=0; j<constraints[i].scope.length; j++)
					myPrint(constraints[i].scope[j]+", ");
				myPrintln("");
				myPrintln("\tconstraint table: ");
				for (int j=0; j<constraints[i].table.length; j++) {
					myPrint("\t\tcost = "+constraints[i].table[j][0]+"\tvalues = ");
					for (int k=1; k<constraints[i].table[j].length; k++) {
						myPrint(constraints[i].table[j][k]+", ");
					}
					myPrintln("");
				}
				myPrintln("");
			}
		}catch (SAXParseException err) {
			myPrintln ("** Parsing error" + ", line " 
					+ err.getLineNumber () + ", uri " + err.getSystemId ());
			myPrintln(" " + err.getMessage ());

		}catch (SAXException e) {
			Exception x = e.getException ();
			((x == null) ? e : x).printStackTrace ();

		}catch (Throwable t) {
			t.printStackTrace ();
		}
	} // end constructor
	
	public void myPrintln(String s) {
		if (DEBUG) 
			System.out.println(agentName+": "+s+"");
	}
	
	public void myPrint(String s) {
		if (DEBUG) 
			System.out.print(agentName+": "+s+"");
	}

	/**
	 * @return the problemName
	 */
	public String getProblemName() {
		return problemName;
	}

	/**
	 * @return the maxConstraintArity
	 */
	public int getMaxConstraintArity() {
		return maxConstraintArity;
	}

	/**
	 * @return the maximize
	 */
	public boolean isMaximize() {
		return maximize;
	}

	/**
	 * @return the nbNeighbors
	 */
	public int getNbNeighbors() {
		return nbNeighbors;
	}

	/**
	 * @return the neighbors
	 */
	public Neighbors[] getNeighbors() {
		return neighbors;
	}

	/**
	 * @return the nbVariables
	 */
	public int getNbVariables() {
		return nbVariables;
	}

	/**
	 * @return the variables
	 */
	public String[][] getVariables() {
		return variables;
	}

	/**
	 * @return the nbConstraints
	 */
	public int getNbConstraints() {
		return nbConstraints;
	}

	/**
	 * @return the constraints
	 */
	public Constraints[] getConstraints() {
		return constraints;
	}

	/**
	 * @return the agentName
	 */
	public String getAgentName() {
		return agentName;
	}
} // end XMLReader class

class Neighbors {
	String agentName;
	String commCost;
	int nbRelatedVariables;
	String[][] relatedVariables;
	
	Neighbors(int n) {
		agentName = new String();
		commCost = new String();
		nbRelatedVariables = n;
		relatedVariables = new String[n][3];
	}
	
	/**
	 * @return the nbRelatedVariables
	 */
	public int getNbRelatedVariables() {
		return nbRelatedVariables;
	}
	
	/**
	 * @return the agentName
	 */
	public String getAgentName() {
		return agentName;
	}

	/**
	 * @return the commCost
	 */
	public String getCommCost() {
		return commCost;
	}

	/**
	 * @return the relatedVariables
	 */
	public String[][] getRelatedVariables() {
		return relatedVariables;
	}
}

class Constraints {
	int arity;
	String constraintName;
	String defaultCost;
	String scope[];
	int table[][];

	Constraints(int a, int nbTuples) {
		arity = 0;
		constraintName = new String();
		defaultCost = new String();
		scope = new String[a];
		table = new int[nbTuples][a+1];
	}

	/**
	 * @return the arity
	 */
	public int getArity() {
		return arity;
	}

	/**
	 * @return the constraintName
	 */
	public String getConstraintName() {
		return constraintName;
	}

	/**
	 * @return the defaultCost
	 */
	public String getDefaultCost() {
		return defaultCost;
	}

	/**
	 * @return the scope
	 */
	public String[] getScope() {
		return scope;
	}

	/**
	 * @return the table
	 */
	public int[][] getTable() {
		return table;
	}
}